#!/usr/bin/env python
# encoding: utf-8
from threading import Thread
import os
import time
import binascii
import configparser

import logging
logging.basicConfig(filename=os.getcwd() + '/log/mqtt.log',
                    format='%(asctime)s - %(levelname)s - %(message)s',
                    level=logging.DEBUG)

import socketio

import serial
import serial.tools.list_ports

class Ser:
    def __init__(self):
        self.configfile = os.getcwd()+'/conf/serial.conf'
        self.ser_init()
        self.find()

    def ser_init(self):
        '''初始化串口'''
        self.serial = serial.Serial()
        self.serial.timeout = 0.5# make sure that the alive event can be checked from time to time
        self.serial_alive = False
        self.serial.port = None
        self.serial.baudrate = 115200
        self.serial.bytesize = 8
        self.serial.parity   = 'N'
        self.serial.stopbits = 1
        self.recv = ''
        self.recv = []
        self.thread_recv = None
        # 串口查询线程
        self.thread_find_open = Thread(target=self.find_open)
        self.thread_find_open.setDaemon(True)
        self.thread_find_open.start()

    def find(self):
        """查询串口"""
        p = None
        port_list = serial.tools.list_ports.comports()
        port_list_info = []
        for p in port_list:
            print('*'*50)
            print(str(p.device))
            print(str(p.serial_number))
        print('*' * 50)

    def set_default_serial(self, device='', serial_number='', baudrate=''):
        """设置默认串口"""
        try:
            config = configparser.RawConfigParser()
            # 添加condition选项
            config.add_section('serial')
            config.set("serial", "device", device)
            config.set("serial", "serial_number", serial_number)
            config.set("serial", "baudrate", baudrate)
            # 写入并保存文件到当前项目文件夹下
            with open(self.configfile, 'w') as configfile:
                config.write(configfile)
        except Exception as e:
            print(e)

    def find_open(self):
        """打开串口
        采用configparser读取配置文件：/conf/serial.ini, 从中获取串口设备的硬件ID[serial_number]，
        再根据当前查询到的串口外设[port_list]进行比对，打开满足设定的串口外设
        """
        # 读取默认串口配置文件
        while not self.serial_alive :
            time.sleep(1)
            config = configparser.ConfigParser()
            config.read(self.configfile)
            port_list = serial.tools.list_ports.comports()
            port = ''
            baudrate = ''
            for p in port_list:
                if str(p.serial_number) == config['serial']['serial_number']:
                    port = p.device
                    baudrate = config['serial']['baudrate']
                    self.set_default_serial(p.device, p.serial_number, self.serial.baudrate)
                    break
            if port and baudrate:
                try:
                    self.serial.port = port
                    self.serial.baudrate = baudrate
                    self.serial.open()
                except serial.SerialException as e:
                    print("Open Seial Error ：%s" % e)
                    # logging.debug("Open Seial Error ：%s"%e)
                else:
                    self.serial_alive = True
                    self.thread_recv = Thread(target=self.recv_hander)
                    self.thread_recv.setDaemon(True)
                    self.thread_recv.start()
                    # self.thread_write = Thread(target=self.write_hander)
                    # self.thread_write.setDaemon(True)
                    # self.thread_write.start()
                    print("打开串口: %s / %s"%(self.serial.port, self.serial.baudrate))
                    # logging.debug("打开串口: %s / %s"%(self.serial.port, self.serial.baudrate))
            else:
                print("串口设备未发现")
                # logging.debug("串口设备未发现")

    def write(self, msg="", _type='str'):
        """串口写入msg,写入需要将字符串转换成bytes"""
        if self.serial_alive:
            if self.serial.isOpen():
                # 十六进制格式字符串转bytes
                # print(type(msg))
                print("串口发送命令：%s"%msg)
                if  _type== 'bytes':
                    print("发送[bytes]")
                    self.serial.write(msg)
                if  _type== 'str':
                    print('发送[str]')
                    self.serial.write(msg.encode('utf-8'))
                if  _type== 'hex':
                    print("发送[hex]")
                    self.serial.write(binascii.unhexlify(msg))
        else:
            print("serial is not open!!!")

    def writeBytes(self, msg):
        """串口写入msg,写入需要将字符串转换成bytes"""
        if self.serial_alive:
            if self.serial.isOpen():
                try:
                    print("串口发送命令：%s"%msg)
                    self.serial.write(msg)
                except Exception as e:
                    print(e)
        else:
            print("serial is not open!!!")

    def recv_hander(self):
        """串口接收, 串口端口异常情况下，关闭串口，并重新开启串口查询功能"""
        while self.serial_alive:
            try:
                n = self.serial.inWaiting()
                if n:
                    b = self.serial.read(n)
                    # bytes转为16进制字符串
                    s = str(binascii.b2a_hex(b).decode('utf-8')).upper()
                    # msg = { "msg":s }
                    self.serial.write(b)
                    print("串口接收:",s)
                    sio.emit("rxd",data='{"msg":%s}'%s)
            except Exception as e:
                print(e)
                logging.warning(e)
                print("串口异常")
                # 关闭串口
                self.serial_alive = False
                self.serial.close()
                print("串口关闭")
                # 重新打开串口查询
                self.thread_find_open = Thread(target=self.find_open)
                self.thread_find_open.setDaemon(True)
                self.thread_find_open.start()
                print("重新查询串口")

    def recv_deal(self):
        '''串口处理命令，监测校验和是否正确'''
        self.recv = self.recv
        start = self.recv.find("AA")
        startwith = self.recv.startswith('AA')
        def find_last(string, s):
            last_position = -1
            while True:
                position = string.find(s, last_position + 1)
                if position == -1:
                    return last_position
                last_position = position
        start_num = len(re.compile('.*?(AA).*?').findall(self.recv))
        if self.recv.startswith('AA'):
            for i in re.compile('.*?(AA).*?').findall(self.recv):
                if not len(self.recv)>4:
                    return
                else:
                    theory_len = int(self.recv[2:4], 16)
                    real_len = len(self.recv) / 2 - 3
                    if not theory_len <= real_len:
                        return
                    else:
                        end = (theory_len+3)*2
                        cmd = self.recv[start:end]
                        self.recv = self.recv[end:]
                        s = 0
                        cmd_sum = cmd[-2:]
                        cmd_data = cmd[4:-2]
                        for i in range(0, len(cmd_data)):
                            if i % 2 == 0:
                                s = s + int(cmd_data[i:i + 2], 16)
                        s = hex(s).upper()[-2:]
                        if s == cmd_sum :
                            print('校验和正确')
                            self.cmd_deal(cmd_data)
                        else:
                            self.recv = ''
                            print('校验错误！！！！！！！！')
                            print('正确：', s, '错误:', cmd_sum)
        else:
            self.recv = self.recv[self.recv.find('AA'):]

    def cmd_deal(self,data):
        addr = data[0:4] + '12'
        ver = data[4:6]
        _type = data[6:8]
        print(addr)
        node = self.nodes.find_node(lora_addr=addr)
        print('-'*20+'串口接收数据包处理'+'-'*20)
        print('匹配到设备：',node)
        print('数据包：',data)
        print('发送地址：',addr)
        print('命令版本：',ver)
        print('命令类型：',_type)
        print('-'*50)
        msg = {}
        if node:
            msg['cid'] = node['cid']
            msg['type'] = 'tap'
            if _type == "01":
                msg['tap'] = '1'
            if _type == "02":
                msg['tap'] = '0'
            self.mqtt.on_publish("17740171896",json.dumps(msg))

    def str_to_hex(self, s):
        """字符串转16进制"""
        return ''.join([hex(ord(c)).replace('0x', '').zfill(2) for c in s])

    def sendToUrl(self, msg):
        print("发送消息到网页")
        url = 'http://localhost:5000/serial_recv'
        body = {"msg": msg}
        headers = {'content-type': "application/json"}
        response = requests.post(url, data = json.dumps(body), headers = headers)

ser = Ser()
sio = socketio.Client()

@sio.on('connect')
def on_connect():
    print('连接上本地socketio')

@sio.on('txd')
def on_message(data):
    print('[txd] %s Receiver :', len(data), data)
    ser.write(msg=data, _type='hex')

@sio.on('disconnect')
def on_disconnect():
    print('disconnected from server')

def run():
    sio.connect('http://localhost:5000')
    sio.wait()
    # mqtt.client_loop()


if __name__ == '__main__':
    # test()
    run()


